use std::{
	collections::BTreeMap,
	path::{Path, PathBuf},
};

use super::*;
use lexer::*;

#[derive(Debug, Clone, Default)]
pub struct Fea {
	pub base: PathBuf,
	pub groups: BTreeMap<String, ClassDef>,
	pub valuerecords: BTreeMap<String, ValueRecordDef>,
	pub anchors: BTreeMap<String, AnchorDef>,
	pub markclass: BTreeMap<String, MarkClass>,
	pub langsys: Vec<LanguageSystem>,
	pub lookups: BTreeMap<String, Lookup>,
	pub features: Vec<Feature>,
	pub anons: Vec<Anon>,
}
impl Fea {
	pub fn new(base: impl AsRef<Path>, content: &str) -> Result<Self, Error> {
		Self::parse(base, &mut Lexer::new(content))
	}
	pub fn parse(base: impl AsRef<Path>, r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut this = Self::default();
		this.base = base.as_ref().to_path_buf();
		this.parse_impl(r)?;
		Ok(this)
	}
	pub fn parse_impl(&mut self, r: &mut Lexer<'_>) -> Result<(), Error> {
		while let Some(token) = r.next() {
			match token {
				Event::Group(name) => {
					let v = ClassDef::parse(r, name)?;
					self.groups.insert(v.name.clone(), v);
				}
				Event::ValueRecordDefKw => {
					let v = ValueRecordDef::parse(r)?;
					self.valuerecords.insert(v.name.clone(), v);
				}
				Event::AnchorDefKw => {
					let v = AnchorDef::parse(r)?;
					self.anchors.insert(v.name.clone(), v);
				}
				Event::MarkClassKw => {
					let v = MarkClass::parse(r)?;
					self.markclass.insert(v.name.clone(), v);
				}
				Event::IncludeKw => {
					r.expect(Event::LParen)?;
					let path = r.read_file()?;
					r.expect(Event::RParen)?;
					r.expect(Event::Semi)?;
					let path = self.base.join(path);
					let content = std::fs::read_to_string(path).map_err(|_| Error::Msg("inlucde not found"))?;
					match self.parse_impl(&mut Lexer::new(&content)) {
						Ok(_) => {}
						Err(Error::Eof) => {}
						Err(e) => return Err(e),
					}
				}
				Event::LanguagesystemKw => {
					self.langsys.push(LanguageSystem::parse(r)?);
				}
				Event::FeatureKw => {
					self.features.push(Feature::parse(r, self.base.clone())?);
				}
				Event::LookupKw => {
					let name = r.expect_ident()?;
					let v = match r.read_next()? {
						Event::UseExtensionKw => {
							r.expect(Event::LBrace)?;
							Lookup::parse(r, name, self.base.clone(), true, Default::default())
						}
						Event::LBrace => Lookup::parse(r, name, self.base.clone(), false, Default::default()),
						e => Err(Error::Unexpect(format!("{:?}", e))),
					}?;
					self.lookups.insert(v.name.clone(), v);
				}
				Event::TableKw => {
					let table = r.expect_ident()?;
					match table {
						"BASE" => _ = Table::parse(r)?,
						"GDEF" => _ = Table::parse(r)?,
						"head" => _ = Table::parse(r)?,
						"hhea" => _ = Table::parse(r)?,
						"name" => _ = Table::parse(r)?,
						"OS/2" => _ = Table::parse(r)?,
						"vhea" => _ = Table::parse(r)?,
						"vmtx" => _ = Table::parse(r)?,
						"STAT" => _ = Table::parse(r)?,
						_ => return Err(Error::Msg("unexcepted table name")),
					}
					_ = r.expect(Event::Ident(table))?;
					_ = r.expect(Event::Semi)?;
				}
				Event::AnonKw => self.anons.push(Anon::parse(r)?),
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(())
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn load_fontinfotest() {
		let content = std::fs::read_to_string("assets/ufo/fontinfotest_v3.ufo/features.fea").unwrap();
		let mut lexer = Lexer::new(&content);
		let _gram = Fea::parse("", &mut lexer).unwrap();
		println!("{:#?}", _gram);
	}
}
